using System;
using System.Collections.Generic;
using FlexiCommerce.Components;
using System.Linq;
using FlexiCommerce.Persistence.NH;
using NHibernate;

namespace FlexiCommerce.Persistence
{
	/// <summary>
	/// Facade for finding and querying items.
	/// </summary>
	/// <typeparam name="TRoot">The type of root page in the system.</typeparam>
	/// <typeparam name="TStart">The type of start page in the system.</typeparam>
    public abstract class GenericFind<TRoot, TStart>
        where TRoot : GenericPersistenceItem
        where TStart : GenericPersistenceItem
    {

        /// <summary>Enumerates parents of the initial item.</summary>
        /// <param name="initialItem">The page whose parents will be enumerated. The page itself will not appear in the enumeration.</param>
        /// <returns>An enumeration of the parents of the initial page.</returns>
        public static IEnumerable<T> EnumerateParents<T>(T initialItem)
            where T : class, IHierarchicalPersistenceItem
        {
            return EnumerateParents<T>(initialItem, null);
        }

        /// <summary>Enumerates parents of the initial item.</summary>
        /// <param name="initialItem">The page whose parents will be enumerated. The page itself will not appear in the enumeration.</param>
        /// <param name="lastAncestor">The last page of the enumeration. The enumeration will contain this page.</param>
        /// <returns>An enumeration of the parents of the initial page. If the last page isn't a parent of the inital page all pages until there are no more parents are returned.</returns>
        public static IEnumerable<T> EnumerateParents<T>(T initialItem, T lastAncestor)
            where T: class, IHierarchicalPersistenceItem
        {
            return EnumerateParents<T>(initialItem, lastAncestor, false);
        }

        /// <summary>Enumerates parents of the initial item.</summary>
        /// <param name="initialItem">The page whose parents will be enumerated. The page itself will appear in the enumeration if includeSelf is applied.</param>
        /// <param name="lastAncestor">The last page of the enumeration. The enumeration will contain this page.</param>
        /// <param name="includeSelf">Include the lanitialItem in the enumeration.</param>
        /// <returns>An enumeration of the parents of the initial page. If the last page isn't a parent of the inital page all pages until there are no more parents are returned.</returns>
        public static IEnumerable<T> EnumerateParents<T>(T initialItem, T lastAncestor, bool includeSelf)
            where T : class, IHierarchicalPersistenceItem
        {
            if (initialItem == null) yield break;

            IHierarchicalPersistenceItem item;
            if (includeSelf)
                item = initialItem;
            else if (initialItem != lastAncestor)
                item = initialItem.Parent;
            else
                yield break;

            while (item != null)
            {
                yield return item as T;
                if (item == lastAncestor)
                    break;
                item = item.Parent;
            }
        }
        public static IEnumerable<IHierarchicalPersistenceItem> EnumerateParents(IHierarchicalPersistenceItem initialItem, IHierarchicalPersistenceItem lastAncestor, bool includeSelf)
        {
            if (initialItem == null) yield break;

            IHierarchicalPersistenceItem item;
            if (includeSelf)
                item = initialItem;
            else if (initialItem != lastAncestor)
                item = initialItem.Parent;
            else
                yield break;

            while (item != null)
            {
                yield return item;
                if (item == lastAncestor)
                    break;
                item = item.Parent;
            }
        }
        /// <summary>Enumerates the trail of items from the last ancestor to the deepest child.</summary>
        /// <param name="deepestChild">The page whose parents will be enumerated. The page itself will appear in the enumeration if includeSelf is applied.</param>
        /// <param name="lastAncestor">The first page of the enumeration.</param>
        /// <param name="includeDeepestChild">Include the deepest child in the enumeration.</param>
        /// <returns>An enumeration of the from the ancestor uptil the deepest child.</returns>
        public static IEnumerable<HierarchicalPersistenceItem> EnumerateBetween(HierarchicalPersistenceItem lastAncestor, HierarchicalPersistenceItem deepestChild, bool includeDeepestChild)
        {
            IList<HierarchicalPersistenceItem> items = ListParents(deepestChild, lastAncestor, includeDeepestChild);
            for (int i = items.Count - 1; i >= 0; --i)
            {
                yield return items[i];
            }
        }

        /// <summary>Creates a list of the parents of the initial item.</summary>
        /// <param name="initialItem">The page whose parents will be enumerated. The page itself will not appear in the enumeration.</param>
        /// <param name="lastAncestor">The last page of the enumeration. The enumeration will contain this page.</param>
        /// <returns>A list of the parents of the initial page. If the last page isn't a parent of the inital page all pages until there are no more parents are returned.</returns>
        public static IList<HierarchicalPersistenceItem> ListParents(HierarchicalPersistenceItem initialItem, HierarchicalPersistenceItem lastAncestor, bool includeInitialItem)
        {
            return new List<HierarchicalPersistenceItem>(EnumerateParents(initialItem, lastAncestor, includeInitialItem));
        }

        /// <summary>Creates a list of the parents of the initial item.</summary>
        /// <param name="initialItem">The page whose parents will be enumerated. The page itself will not appear in the enumeration.</param>
        /// <param name="lastAncestor">The last page of the enumeration. The enumeration will contain this page.</param>
        /// <returns>A list of the parents of the initial page. If the last page isn't a parent of the inital page all pages until there are no more parents are returned.</returns>
        public static IList<HierarchicalPersistenceItem> ListParents(HierarchicalPersistenceItem initialItem, HierarchicalPersistenceItem lastAncestor)
        {
            return new List<HierarchicalPersistenceItem>(EnumerateParents(initialItem, lastAncestor));
        }

        /// <summary>Creates a list of the parents of the initial item.</summary>
        /// <param name="initialItem">The page whose parents will be enumerated. The page itself will not appear in the enumeration.</param>
        /// <returns>A list of the parents of the initial page.</returns>
        public static IList<HierarchicalPersistenceItem> ListParents(HierarchicalPersistenceItem initialItem)
        {
            return new List<HierarchicalPersistenceItem>(EnumerateParents(initialItem));
        }

        /// <summary>Enumerates items in a tree hierarchy.</summary>
        /// <param name="rootItem">The parent item whose child items to enumerate. The item itself is not returned.</param>
        /// <returns>An enumeration of all children of an item.</returns>
        public static IEnumerable<GenericPersistenceItem> EnumerateTree(GenericPersistenceItem rootItem)
        {
            yield return rootItem;
            foreach (GenericPersistenceItem item in EnumerateChildren(rootItem))
            {
                yield return item;
            }
        }

        /// <summary>Enumerates child items and their children, and so on.</summary>
        /// <param name="item">The parent item whose child items to enumerate. The item itself is not returned.</param>
        /// <returns>An enumeration of all children of an item.</returns>
        public static IEnumerable<IHierarchicalPersistenceItem> EnumerateChildren(IHierarchicalPersistenceItem item)
        {

            foreach (IHierarchicalPersistenceItem child in item.Children)
            {
                yield return child;
                foreach (IHierarchicalPersistenceItem childItem in EnumerateChildren(child))
                    yield return childItem;
            }
        }

        /// <summary>Filters items not of the given type and return an enumeration.</summary>
        /// <typeparam name="T">The typeof item to return.</typeparam>
        /// <param name="items">The input enumeration to filter.</param>
        /// <returns>An enumeration items of the supplied type.</returns>
        public static IEnumerable<T> OfType<T>(IEnumerable<GenericPersistenceItem> items)
            where T : class
        {
            foreach (GenericPersistenceItem item in items)
            {
                if (item is T)
                    yield return item as T;
            }
        }


        public static IQueryable<GenericPersistenceItem> GenericPersistenceItem
        {
            get
            {
               return NHibernate.Linq.NHibernateExtensions.Linq<GenericPersistenceItem>(Context.Current.Resolve<ISessionProvider>().OpenSession.Session);
            }
        }

        public static IQueryable<Language> Languages
        {
            get
            {
                IQueryable<Language> languages = NHibernate.Linq.NHibernateExtensions.Linq<Language>(Context.Current.Resolve<ISessionProvider>().OpenSession.Session);
                return languages;
            }
        }

        public static IQueryable<Category> Categories
        {
            get
            {
                ISession session = Context.Current.Resolve<ISessionProvider>().OpenSession.Session;
                //session.EnableFilter("").
                return NHibernate.Linq.NHibernateExtensions.Linq<Category>(session);
            }
        }
        public static IQueryable<CategoryDetails> CategoryDetails
        {
            get
            {
                ISession session = Context.Current.Resolve<ISessionProvider>().OpenSession.Session;
                return NHibernate.Linq.NHibernateExtensions.Linq<CategoryDetails>(session);
            }
        }
        public static IQueryable<Product> Products
        {
            get
            {
                ISession session = Context.Current.Resolve<ISessionProvider>().OpenSession.Session;
                //session.EnableFilter("").
                return NHibernate.Linq.NHibernateExtensions.Linq<Product>(session);
            }
        }
        public static IQueryable<ProductDetails> ProductDetails
        {
            get
            {
                ISession session = Context.Current.Resolve<ISessionProvider>().OpenSession.Session;
                return NHibernate.Linq.NHibernateExtensions.Linq<ProductDetails>(session);
            }
        }
        public static IQueryable<Image> Images
        {
            get
            {
                ISession session = Context.Current.Resolve<ISessionProvider>().OpenSession.Session;
                return NHibernate.Linq.NHibernateExtensions.Linq<Image>(session);
            }
        }
        public static IQueryable<Components.Configuration> Configurations
        {
            get
            {
                ISession session = Context.Current.Resolve<ISessionProvider>().OpenSession.Session;
                return NHibernate.Linq.NHibernateExtensions.Linq<Components.Configuration>(session);
            }
        }

        /// <summary>Determines wether an item is below a certain ancestral item.</summary>
        /// <param name="item">The item to check for beeing a child or descendant.</param>
        /// <param name="ancestor">The item to check for beeing parent or ancestor.</param>
        /// <returns>True if the item is descendant the ancestor.</returns>
        public static bool IsDescendant(HierarchicalPersistenceItem item, HierarchicalPersistenceItem ancestor)
        {
            if (item == null) throw new ArgumentNullException("item");
            if (ancestor == null) throw new ArgumentNullException("ancestor");

            return In(ancestor, EnumerateParents(item));
        }

        /// <summary>Gets an item at a certain level from the last ancestor.</summary>
        /// <param name="initialItem">The item at the greatest depth.</param>
        /// <param name="lastAncestor">The root node for this operation.</param>
        /// <param name="depth">The depth counting from the last ancestor to retrieve an item from.</param>
        /// <returns>The item at the specified depth, or null if this is outside bounds.</returns>
        public static HierarchicalPersistenceItem AtLevel(HierarchicalPersistenceItem initialItem, HierarchicalPersistenceItem lastAncestor, int depth)
        {
            IList<HierarchicalPersistenceItem> path = ListParents(initialItem, lastAncestor, true);
            int index = path.Count - depth;
            if (index >= 0 && index < path.Count)
                return path[index];
            return null;
        }

        /// <summary>Determines wether an item is below a certain ancestral item or is the ancestral item.</summary>
        /// <param name="item">The item to check for beeing a child or descendant.</param>
        /// <param name="ancestor">The item to check for beeing parent or ancestor.</param>
        /// <returns>True if the item is descendant the ancestor.</returns>
        public static bool IsDescendantOrSelf(HierarchicalPersistenceItem item, HierarchicalPersistenceItem ancestor)
        {
            if (item == null) throw new ArgumentNullException("item");
            if (ancestor == null) throw new ArgumentNullException("ancestor");

            return item == ancestor || In(ancestor, EnumerateParents(item));
        }

        /// <summary>Determines wether an item is in a enumeration of items.</summary>
        /// <param name="wantedItem">The item to look for.</param>
        /// <param name="linedUpItems">The items to look among.</param>
        /// <returns>True if the item is in the enumeration of items.</returns>
        public static bool In(HierarchicalPersistenceItem wantedItem, IEnumerable<HierarchicalPersistenceItem> linedUpItems)
        {
            if (wantedItem == null) throw new ArgumentNullException("wantedItem");
            if (linedUpItems == null) throw new ArgumentNullException("linedUpItems");

            foreach (GenericPersistenceItem enumeratedItem in linedUpItems)
            {
                if (enumeratedItem == wantedItem)
                    return true;
            }
            return false;
        }
    }
}
